当我们对运行时资源进行缓存操作时,并没有一个统一标准来指明该资源是有效且可以被缓存的,在 Workbox 中,我们可以使用 workbox-cacheable-response 模块,根据 Response Status CodeResponse Headers 来决定是否对资源进行缓存操作。基于此,本章我们将讨论 workbox-cacheable-response 模块的使用。

# 基本使用

workbox.routing.registerRoute(
  new RegExp('^https://third-party\\.example\\.com/images/'),
  new workbox.strategies.CacheFirst({
    cacheName: 'image-cache',
    plugins: [
      new workbox.cacheableResponse.Plugin({
        statuses: [0, 200],
        headers: {
          'Is-Cacheable': 'true',
          'X-Is-Cacheable': 'true'
        }
      })
    ]
  })
);

上例中,我们通过在 workbox.strategies.CacheFirstplugins 属性中添加 workbox.cacheableResponse.Plugin 实例来设置指定路由可被缓存的条件,该路由的响应将在 Response Status Code 为 0 或 200,并且 Response Headers 中包含 Is-Cacheable=trueX-Is-Cacheable=true 时进行缓存处理。另外,亦可单独指定 statuses 或 headers 属性以达到只根据 Response Status CodeResponse Headers 来判断资源是否可被缓存的目的。

workbox.cacheableResponse.Plugin 的使用非常简单,由于它只能在 workbox.strategies 模块的 CacheFirstNetworkFirstStaleWhileRevalidate 中使用,因此我们可直接使用 workbox.cacheableResponse.CacheableResponse 在自定义的请求策略中完成资源是否可缓存的逻辑处理,比如:

const cacheable = new workbox.cacheableResponse.CacheableResponse({
  statuses: [0, 200],
  headers: {
    'Is-Cacheable': 'true',
    'X-Is-Cacheable': 'true'
  }
});

const response = await fetch('/path/to/api');
if (cacheable.isResponseCacheable(response)) {
  const cache = await caches.open('api-cache');
  cache.put(response.url, response);
} else {
}

# 默认处理

workbox.strategies 模块的 CacheFirstNetworkFirstStaleWhileRevalidate 中,如果我们未设置 workbox.cacheableResponse.Plugin,那么它们将根据以下规则来对响应是否可缓存进行处理:

  • CacheFirst 中,当 Response Status Code200 时,则对响应进行缓存处理。
  • NetworkFirstStaleWhileRevalidate中,当 Response Status Code0200 时,则对响应进行缓存处理。

上述设置中,CacheFirst 之所以不认可 Response Status Code 为 0 时的响应,主要是因为:

  • Response Status Code 为 0 时,即表明该响应为不透明响应,我们无法获得此类响应的任何有效信息,这也意味着我们无法判断该响应是否包含错误信息;
  • 如果上述的不透明响应包含错误信息且 CacheFirst 认可并缓存该响应,那么除非用户手动清除缓存或缓存过期后被清除,否则用户得到的永远是含有错误信息的响应;
  • 而在 NetworkFirstStaleWhileRevalidate 中,即使在某一刻缓存了含有错误信息的响应,它依旧可以在未来被正确的响应所替换。

# 不透明响应

上文我们提到了不透明响应,本小节将对其就行简单介绍。

不透明响应是 Fetch API 标准的一部分,表示在未启用 CORS 时远程服务器对请求的响应。对于此类响应,无论请求成功与否,该响应的 Response Status Code 始终为 0,且我们无法访问 Response 中的任何属性,或调用如 json、text 等构成 Body 的各种方法。它主要作用于以下标签来加载跨域资源:

  • <script>
  • <link rel="stylesheet">
  • <img><video><audio>
  • <object><embed>
  • <iframe>

如果想要确定页面上的特定资源是否可以使用不透明响应,我们需要检查相关规范。比如,HTML 规范解释了不透明响应可作用于 <script> 标签,但依旧有一些限制以防止错误信息泄漏。

  • 由于不透明响应的 Response Status Code 始终为 0,且 Cache API 中的 add、addAll 方法不接受不在 2XX 范围内的 Response Status Code,因此我们应使用 put 方法来缓存不透明响应,比如:
const request = new Request('https://third-party-no-cors.com/', { mode: 'no-cors' });
fetch(request).then(response => cache.put(request, response));

为避免跨域信息的泄漏,浏览器在计算存储空间配额限制时会对不透明响应的大小进行填充(具体大小由浏览器决定,比如 Google Chrome 中每个不透明响应的存储空间最少为 7 MB),因此我们在对不透明响应进行缓存时,应时刻牢记这一点,因为根据响应的实际大小,可能很快就会突破存储空间的配额限制。

# 总结

本章我们首先对 workbox-cacheable-response 的使用进行了简单介绍,然后讨论了 workbox.strategies 模块中 CacheFirstNetworkFirstStaleWhileRevalidate对响应是否可缓存的默认处理,最后我们对不透明响应进行了讨论。下一章,我们将对Workbox` 缓存更新广播进行讨论。

阅读全文